#include "clib_config.h"
#include "clib_type.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef enum 
{
    clib_config_pars_none,
    clib_config_pars_obj_begin,
    clib_config_pars_obj_end,
    clib_config_pars_str,
    clib_config_pars_str_end,
    clib_config_pars_note,
    clib_config_pars_list_begin,
    clib_config_pars_list_end,
    clib_config_pars_list_next,
    clib_config_pars_sign
} clib_opt_pars_t;

typedef struct clib_string_pars_s
{
    i08_t          *pos;
    i32_t           len;
    clib_opt_pars_t opt;
} clib_string_pars_t;

static inline i08_t*
clib_string_find_filed_sign(i08_t* line,clib_opt_pars_t opt) 
{
    /** TODO 处理  引号("") **/
    i08_t *end;
    i08_t *begin = line;
    while(*begin) {
        if(*begin == '=' || 
           *begin == '{' ||
           *begin == ';' ||
           *begin == '(' ||
           (opt != clib_config_pars_sign && *begin == ' ')) {
            break;
        }
        begin++;
    }
    begin--;
    // if(*begin == ' ' || !*begin) {
    //     begin--;
    // }
    /** TODO验证长度 **/
    return begin;

}

static inline clib_opt_pars_t
clib_string_find_sign(i08_t* line) 
{
    i08_t *begin = line;

    if(*begin == '#') {
        return clib_config_pars_note;
    } else if(*begin == '=') {
        return clib_config_pars_sign;
    } else if(*begin == '(') {
        return clib_config_pars_list_begin;
    } else if(*begin == ')') {
        return clib_config_pars_list_end;
    } else if(*begin == '{') {
        return clib_config_pars_obj_begin;
    } else if(*begin == '}') {
        return clib_config_pars_obj_end;
    } else if(*begin == ';') {
        return clib_config_pars_str_end;
    } else if(*begin == ',') {
        return clib_config_pars_list_next;
    } else if(*begin == 0 || *begin == '\r' || *begin == '\n') {
        return clib_config_pars_none;
    }

    return clib_config_pars_str;
}

static inline i32_t
clib_pars_item_value(clib_config_t* node,i08_t* value,i32_t len)
{
    i32_t is_number = 1;
    i32_t is_float  = 0;
    i08_t cache[64] = {0};

    for(int i = 0; i < len; i++)
    {
        if((value[i] < '0' || value[i] > '9')) 
        {
            if(value[i] == '.') {
                is_float = 1;
                continue;
            } else if (i== 0 && value[i] == '-') {
                continue;
            } else {
                is_number = 0;
                break;
            }
        }
    }
    if(is_number) {
        memcpy(cache,value,len);
        if(is_float) {
            node->value.d64 = atof(cache);
            node->type = clib_config_type_d64;
        } else {
            node->value.i64 = atoll(cache);
            node->type = clib_config_type_i64;
        }
    } else {
        i08_t* str = clib_config_malloc(len + 1);
        memcpy(str,value,len);
        str[len] = 0;
        node->value.ptr = str;
    }

    return 0;
}

static inline i32_t
clib_pars_item_check(clib_string_pars_t* pars,i32_t depth,i32_t index) 
{
    clib_string_pars_t *pa;
    i32_t total = 0;
    for(i32_t i = index; i < depth; i++) {
        pa = &pars[i];
        switch (pa->opt)
        {
        case clib_config_pars_none:
            
            break;
        case clib_config_pars_obj_begin:
            return (total + 1);
            break;
        case clib_config_pars_obj_end:
            return (total + 1);
            
            break;
        case clib_config_pars_note:
            return (total + 1);
            break;
        case clib_config_pars_str:
            
            break;
        case clib_config_pars_str_end:
            return (total + 1);
            break;
        case clib_config_pars_list_begin:
            return (total + 1);
            break;
        case clib_config_pars_list_end:
            
            break;
        case clib_config_pars_list_next:
            return (total + 1);
            break;
        case clib_config_pars_sign:
           
            break;
        default:
            break;
        }
        total++;
    }
    return total;
}

static inline i32_t
clib_config_deal_item(clib_string_pars_t* pars,i32_t nums,clib_config_t** item,i32_t* level)
{
    int len = 0;
    i32_t phase = 0;
    clib_config_t* root = item[(*level) - 1];
    clib_config_t* node = NULL;
    for(i32_t i = 0; i < nums; i++) {
        clib_string_pars_t* pa = &pars[i];
        switch (pa->opt)
        {
        case clib_config_pars_none:
            
            break;
        case clib_config_pars_obj_begin:
            if(!node) {
                node = clib_config_malloc(sizeof(clib_config_t));
                memset(node, 0, sizeof(clib_config_t));
            }
            node->type = clib_config_type_obj;
            clib_config_add_child(root,node);
            item[*level] = node;
            root = node;
            (*level)++;
            break;
        case clib_config_pars_obj_end:
            (*level)--;
            root = item[(*level) - 1];
            break;
        case clib_config_pars_str:
        {
            if(!node) {
                node = clib_config_malloc(sizeof(clib_config_t));
                memset(node, 0, sizeof(clib_config_t));
            }
            if(!phase) {
                memcpy(node->key,pa->pos,pa->len >=CLIB_CONFIG_FILED_MAX ? (CLIB_CONFIG_FILED_MAX-1) : pa->len);
                phase = 1;
            } else {
                if(node->type == clib_config_type_str) {
                    clib_pars_item_value(node,pa->pos,pa->len);
                    clib_config_add_child(root,node);
                } else {
                    memcpy(node->data,pa->pos,pa->len >=CLIB_CONFIG_FILED_MAX ? (CLIB_CONFIG_FILED_MAX-1) : pa->len);
                }
            }
        }
            break;
        case clib_config_pars_str_end:
            
            break;
        case clib_config_pars_list_begin:
            if(!node) {
                node = clib_config_malloc(sizeof(clib_config_t));
                memset(node, 0, sizeof(clib_config_t));
            }
            node->type = clib_config_type_list;
            clib_config_add_child(root,node);
            item[*level] = node;
            root = node;
            (*level)++;
            break;
        case clib_config_pars_list_end:
            (*level)--;
            root = item[(*level) - 1];
            break;
        case clib_config_pars_sign:
            if(node) {
                node->type = clib_config_type_str;
            }
            break;
        default:
            break;
        }
    }

    return 0;   
}

static inline i32_t
clib_config_string_pars_sign(i08_t* line, clib_string_pars_t* pars,i32_t depth)
{
    i32_t idx = 0;
    i08_t *pos;
    clib_opt_pars_t opt = clib_config_pars_none;
    clib_opt_pars_t ropt = clib_config_pars_none;
    char* begin = (char*)line;
    char* end   = (char*)(line + strlen(line));

    while(begin <= end && idx < depth) {
        /** 清除空格 **/
        while(*begin == ' ') {
            begin++;
        }
        ropt = opt;

        opt = clib_string_find_sign(begin);

        switch (opt)
        {
        case clib_config_pars_none:
            begin++;
            break;
        case clib_config_pars_note:
            pars[idx].pos = begin;
            pars[idx].len = strlen(begin);
            pars[idx].opt = clib_config_pars_note;
            idx++;
            return idx;
            break;
        case clib_config_pars_obj_begin:
            pars[idx].pos = begin;
            pars[idx].len = 1;
            pars[idx].opt = clib_config_pars_obj_begin;
            idx++;
            begin++;
            break;
        case clib_config_pars_obj_end:
            pars[idx].pos = begin;
            pars[idx].len = 1;
            pars[idx].opt = clib_config_pars_obj_end;
            idx++;
            begin++;
            break;
        case clib_config_pars_str:
            pos = clib_string_find_filed_sign(begin,ropt);
            pars[idx].pos = begin;
            pars[idx].len = (pos - begin) + 1;
            pars[idx].opt = clib_config_pars_str;
            idx++;
            begin = pos + 1;
            break;
        case clib_config_pars_str_end:
            pars[idx].pos = begin;
            pars[idx].len = 1;
            pars[idx].opt = clib_config_pars_str_end;
            idx++;
            begin++;
            break;
        case clib_config_pars_list_begin:
            pars[idx].pos = begin;
            pars[idx].len = 1;
            pars[idx].opt = clib_config_pars_list_begin;
            idx++;
            begin++;
            break;
        case clib_config_pars_list_end:
            pars[idx].pos = begin;
            pars[idx].len = 1;
            pars[idx].opt = clib_config_pars_list_end;
            idx++;
            begin++;
            break;
        case clib_config_pars_list_next:
            pars[idx].pos = begin;
            pars[idx].len = 1;
            pars[idx].opt = clib_config_pars_list_next;
            idx++;
            begin++;
            break;
        case clib_config_pars_sign:
            pars[idx].pos = begin;
            pars[idx].len = 1;
            pars[idx].opt = clib_config_pars_sign;
            idx++;
            begin++;
            break;      
        default:
            break;
        }
    }
   
   return idx;
}

static inline i08_t*
clib_config_pars_line_sign(const i08_t* line,i32_t len,clib_opt_pars_t* opt)
{
    char* begin = (char*)line;
    char* end   = (char*)(line + len);

    while(begin <= end) {

        if(*begin == '#') {
            *opt = clib_config_pars_note;
            break;
        } else if(*begin == '=') {
            *opt = clib_config_pars_sign;
            break;
        } else if(*begin == '(') {
            *opt = clib_config_pars_list_begin;
            break;
        } else if(*begin == ')') {
            *opt = clib_config_pars_list_end;
            break;
        } else if(*begin == '{') {
            *opt = clib_config_pars_obj_begin;
            break;
        } else if(*begin == '}') {
            *opt = clib_config_pars_obj_end;
            break;
        } else if(*begin != ' ') {
            *opt = clib_config_pars_str;
            break;
        } 

        begin++;
    }
    return begin;
}

static inline i08_t*
clib_config_pars_block(clib_config_t* node,clib_opt_pars_t opt,i08_t* data,FILE* fh,i32_t depth)
{
    /** 先处理data **/
    i08_t line[CLIB_CONFIG_LINE_MAX];
}

i32_t
clib_config_add_child(clib_config_t* root,clib_config_t* node) 
{
    clib_config_t* item;
    if(!root || !node) {
        return -1;
    }

    if(!(root->type == clib_config_type_obj || root->type == clib_config_type_list)) {
        return -1;
    }

    if(root->value.obj == NULL) {
        root->value.obj = node; 
    } else {
        item = root->value.obj;
        while(item->next) {
           item = item->next;
        }
        item->next = node;
    }

    return 0;
}

static inline i32_t
clib_config_add_node(clib_config_t* root,clib_config_t* node) 
{
    clib_config_t* item;
    if(!root || !node) {
        return -1;
    }

    if(root->type != clib_config_type_obj) {
        return -1;
    }

    if(root->next == NULL) {
        root->next = node;
    } else {
        item = root->next;
        while (item->next) {
            item = item->next;
        }
        item->next = node;
    }

    return 0;
}

void
clib_config_init(clib_config_t* cfg)
{
    if(cfg) {
        memset(cfg, 0, sizeof(clib_config_t));
    
        cfg->type = clib_config_type_obj;
    }
}

i32_t
clib_config_read(clib_config_t* cfg,const char* path)
{

    i32_t depth = 0;
    i32_t nums  = 0;
    i32_t deal  = 0;
    clib_string_pars_t *pa;
    clib_string_pars_t pars[CLIB_CONFIG_DEPTH];
    clib_config_t     *item[CLIB_CONFIG_DEPTH] = {0};
    i08_t line[CLIB_CONFIG_LINE_MAX];
    i32_t length;
    if(!path) {
        return -1;
    }

    FILE* fh = fopen(path,"r");
    if(!fh) {
        return -1;
    }

    item[depth++] = cfg;
    
    while(fgets(line, sizeof(line), fh) != NULL) {

        i32_t idx = 0;
        nums = clib_config_string_pars_sign(line,pars,CLIB_CONFIG_DEPTH);

        do {
            deal = clib_pars_item_check(pars,nums,idx);

            clib_config_deal_item(&pars[idx], deal, item, &depth);

            idx += deal;
        } while (idx < nums);
    }

    return 0;
}

clib_config_t*
clib_config_find_child(clib_config_t *node,const char* name) 
{
    clib_config_t* item;
    
    if(!node || !name) {
        return NULL;
    }

    if(!(node->type == clib_config_type_obj || node->type == clib_config_type_list)) {
        return NULL;
    }

    if(node->value.obj == NULL) {
        return NULL; 
    } else {
        item = node->value.obj;
        while(item) {
            
            if(!memcmp(item->key,name,CLIB_CONFIG_FILED_MAX)) {
                return item;
            }

            item = item->next;
        }
    }
    
    return NULL;
}

clib_config_t*
clib_config_item_next(clib_config_t *item)
{
    return item->next;
}

i32_t
clib_config_count_item_by_name(clib_config_t *node,const char* name)
{
    clib_config_t* item;

    i32_t cnt = 0;
    
    if(!node || !name) {
        return 0;
    }

    if(!(node->type == clib_config_type_obj || node->type == clib_config_type_list)) {
        return 0;
    }

    if(node->value.obj == NULL) {
        return 0; 
    } else {
        item = node->value.obj;
        while(item) {
            
            if(!memcmp(item->key,name,CLIB_CONFIG_FILED_MAX)) {
                cnt++;
            }

            item = item->next;
        }
    }

    return cnt;
}

clib_config_t*
clib_config_get_item_by_index(clib_config_t *node, const char* name, i32_t index)
{
    clib_config_t* item;

    i32_t cnt = 0;
    
    if(!node || !name) {
        return NULL;
    }

    if(!(node->type == clib_config_type_obj || node->type == clib_config_type_list)) {
        return NULL;
    }

    if(node->value.obj == NULL) {
        return NULL; 
    } else {
        item = node->value.obj;
        while(item) {
            
            if(!memcmp(item->key, name, strlen(name))) {
                cnt++;
                if(cnt == index) {
                    return item;
                }
            }
            item = item->next;
        }
    }

    return NULL;
}

i32_t
clib_config_get_item_by_name(clib_config_t *node, clib_config_t **elem, const char* name)
{
    i32_t cnt = 0;
    clib_config_t *item;
    if(!node || !name) {
        return 0;
    }

    if(!(node->type == clib_config_type_obj || node->type == clib_config_type_list)) {
        return 0;
    }

    if(node->value.obj == NULL) {
        return 0; 
    } else {
        item = node->value.obj;
        while(item) {
            
            if(!memcmp(item->key,name,CLIB_CONFIG_FILED_MAX)) {
                elem[cnt++] = item;
            }
            item = item->next;
        }
    }

    return cnt;
}

i32_t
clib_config_all_child(clib_config_t *node, clib_config_t **elem)
{
    i32_t cnt = 0;
    clib_config_t *item;

    if(!node) {
        return 0;
    }

    if(!(node->type == clib_config_type_obj || node->type == clib_config_type_list)) {
        return 0;
    }

    if(node->value.obj == NULL) {
        return 0; 
    } else {
        item = node->value.obj;
        while(item) {
            elem[cnt++] = item;

            item = item->next;
        }
    }

    return cnt;
}

clib_config_t*
clib_config_first_child(clib_config_t *root)
{
    clib_config_t* item = NULL;
    if(root->type == clib_config_type_obj 
        || root->type == clib_config_type_list) {
       
        item = root->value.obj;
    }
    return item;
}

i32_t
clib_config_write(clib_config_t* cfg, const char* path) {
    return 0;
}

static void
clib_config_clean_item(clib_config_t* item) {
    clib_config_t* next;
    if(item) {
        /** TODO **/
        next = item->next;
        do {
            if(item->type == clib_config_type_obj) {
                clib_config_clean_item((clib_config_t*)item->value.obj);
                //clib_config_free(item->value.obj);
            } else if(item->type == clib_config_type_list) {
                clib_config_clean_item((clib_config_t*)item->value.obj);
                //clib_config_free(item->value.obj);
            } else if(item->type == clib_config_type_str) {
                clib_config_free(item->value.ptr);
            } else {
                /** TODO **/
            }
            clib_config_free(item);
            item = next;
            if(next) {
                next = item->next;
            }
            
        } while(item); 
    }
}

void
clib_config_clean(clib_config_t* cfg) {
    clib_config_t* item;
    if(cfg) {
        item = cfg->value.obj;
        clib_config_clean_item(item);
    } 
}

i32_t
clib_config_item_to_string(clib_config_t* item)
{
    u08_t *string;
    if(!item) {
        return -1;
    }

    if(item->type == clib_config_type_list|| 
       item->type == clib_config_type_obj ||
       item->type == clib_config_type_none||) {
        return -1;
    }

    if(item->type == clib_config_type_str) {
        return 0;
    }

    string = clib_config_malloc(32);

    if(item->type == clib_config_type_i64) {
        sprintf(string,"%ld",item->value.i64);
    } else if(item->type == clib_config_type_d64) {
        sprintf(string,"%f",item->value.d64);
    }
    item->type = clib_config_type_str;
    item->value.str = string;
    
    return 0;
}